// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "Components/PawnComponent.h"
#include "Cosmetics/LyraCosmeticAnimationTypes.h"
#include "LyraCharacterPartTypes.h"
#include "Net/Serialization/FastArraySerializer.h"

#include "LyraPawnComponent_CharacterParts.generated.h"

class ULyraPawnComponent_CharacterParts;
namespace EEndPlayReason { enum Type : int; }
struct FGameplayTag;
struct FLyraCharacterPartList;

class AActor;
class UChildActorComponent;
class UObject;
class USceneComponent;
class USkeletalMeshComponent;
struct FFrame;
struct FNetDeltaSerializeInfo;

// 当生成的角色部件列表发生变化时调用的委托
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLyraSpawnedCharacterPartsChanged, ULyraPawnComponent_CharacterParts*, ComponentWithChangedParts);

//////////////////////////////////////////////////////////////////////

/**
 * 单个已应用的角色部件
 */
USTRUCT()
struct FLyraAppliedCharacterPartEntry : public FFastArraySerializerItem
{
	GENERATED_BODY()

	FLyraAppliedCharacterPartEntry()
	{}

	FString GetDebugString() const; // 获取调试字符串

private:
	friend FLyraCharacterPartList;
	friend ULyraPawnComponent_CharacterParts;

private:
	// 所代表的角色部件
	UPROPERTY()
	FLyraCharacterPart Part;

	// 返回给用户的句柄索引（仅服务器）
	UPROPERTY(NotReplicated)
	int32 PartHandle = INDEX_NONE;

	// 生成的Actor实例（仅客户端）
	UPROPERTY(NotReplicated)
	TObjectPtr<UChildActorComponent> SpawnedComponent = nullptr;
};

//////////////////////////////////////////////////////////////////////

/**
 * 已应用角色部件的复制列表
 */
USTRUCT(BlueprintType)
struct FLyraCharacterPartList : public FFastArraySerializer
{
	GENERATED_BODY()

	FLyraCharacterPartList()
		: OwnerComponent(nullptr)
	{
	}

public:
	//~FFastArraySerializer 契约
	void PreReplicatedRemove(const TArrayView<int32> RemovedIndices, int32 FinalSize);
	void PostReplicatedAdd(const TArrayView<int32> AddedIndices, int32 FinalSize);
	void PostReplicatedChange(const TArrayView<int32> ChangedIndices, int32 FinalSize);
	//~FFastArraySerializer 契约结束

	bool NetDeltaSerialize(FNetDeltaSerializeInfo& DeltaParms)
	{
		return FFastArraySerializer::FastArrayDeltaSerialize<FLyraAppliedCharacterPartEntry, FLyraCharacterPartList>(Entries, DeltaParms, *this);
	}

	FLyraCharacterPartHandle AddEntry(FLyraCharacterPart NewPart);
	void RemoveEntry(FLyraCharacterPartHandle Handle);
	void ClearAllEntries(bool bBroadcastChangeDelegate);

	FGameplayTagContainer CollectCombinedTags() const;

	void SetOwnerComponent(ULyraPawnComponent_CharacterParts* InOwnerComponent)
	{
		OwnerComponent = InOwnerComponent;
	}
	
private:
	friend ULyraPawnComponent_CharacterParts;

	bool SpawnActorForEntry(FLyraAppliedCharacterPartEntry& Entry);
	bool DestroyActorForEntry(FLyraAppliedCharacterPartEntry& Entry);

private:
	// 装备条目的复制列表
	UPROPERTY()
	TArray<FLyraAppliedCharacterPartEntry> Entries;

	// 包含此列表的组件
	UPROPERTY(NotReplicated)
	TObjectPtr<ULyraPawnComponent_CharacterParts> OwnerComponent;

	int32 PartHandleCounter = 0; // 句柄计数器
};

template<>
struct TStructOpsTypeTraits<FLyraCharacterPartList> : public TStructOpsTypeTraitsBase2<FLyraCharacterPartList>
{
	enum { WithNetDeltaSerializer = true };
};

//////////////////////////////////////////////////////////////////////

/**
 * 一个组件，处理在所有客户端上生成附加到拥有Pawn的化妆角色
 */
UCLASS(meta=(BlueprintSpawnableComponent))
class ULyraPawnComponent_CharacterParts : public UPawnComponent
{
	GENERATED_BODY()

public:
	ULyraPawnComponent_CharacterParts(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());

	//~UActorComponent 接口
	virtual void BeginPlay() override;
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
	virtual void OnRegister() override;
	//~UActorComponent 接口结束

	/**
	 * 将角色部件添加给拥有此定制组件的角色，应仅在权限端调用
	 */
	UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category=Cosmetics)
	FLyraCharacterPartHandle AddCharacterPart(const FLyraCharacterPart& NewPart);

	/**
	 * 从拥有此定制组件的角色移除先前添加的角色部件，应仅在权限端调用
	 */
	UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category=Cosmetics)
	void RemoveCharacterPart(FLyraCharacterPartHandle Handle);

	/**
	 * 移除所有已添加的角色部件，应仅在权限端调用
	 */
	UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category=Cosmetics)
	void RemoveAllCharacterParts();

	/**
	 * 获取从此组件生成的所有角色部件的列表
	 */
	UFUNCTION(BlueprintCallable, BlueprintPure=false, BlueprintCosmetic, Category=Cosmetics)
	TArray<AActor*> GetCharacterPartActors() const;

	// 如果父Actor派生自ACharacter，则返回Mesh组件，否则返回nullptr
	USkeletalMeshComponent* GetParentMeshComponent() const;

	// 返回用于附加生成的Actor的场景组件
	// 如果父Actor派生自ACharacter，我们将使用Mesh组件，否则使用根组件
	USceneComponent* GetSceneComponentToAttachTo() const;

	/**
	 * 返回来自附加角色部件的组合游戏标签集，可选地仅过滤以指定根开头的标签
	 */
	UFUNCTION(BlueprintCallable, BlueprintPure=false, BlueprintCosmetic, Category=Cosmetics)
	FGameplayTagContainer GetCombinedTags(FGameplayTag RequiredPrefix) const;

	void BroadcastChanged(); // 广播变更

public:
	// 当生成的角色部件列表发生变化时将调用的委托
	UPROPERTY(BlueprintAssignable, Category=Cosmetics, BlueprintCallable)
	FLyraSpawnedCharacterPartsChanged OnCharacterPartsChanged;

private:
	// 角色部件列表
	UPROPERTY(Replicated, Transient)
	FLyraCharacterPartList CharacterPartList;

	// 根据角色部件化妆标签，为动画选择身体风格网格体的规则
	UPROPERTY(EditAnywhere, Category=Cosmetics)
	FLyraAnimBodyStyleSelectionSet BodyMeshes;
};